package com.ybear.mvp.model;

import androidx.annotation.NonNull;
import androidx.annotation.Nullable;

import com.ybear.mvp.annotations.Model;
import com.ybear.mvp.util.MvpAnn;
import com.ybear.mvp.util.MvpUtil;

import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

public final class ModelManage {
    //key存储model的名称，val存储调用持有者的信息"
    private final Map<String, HMInfo> mHM = new ConcurrentHashMap<>();

    private ModelManage() {}
    public static ModelManage get() { return HANDLER.I; }
    private static final class HANDLER {
        private static final ModelManage I = new ModelManage();
    }

    /**
     * 实例当前类中所有成员变量
     * 成员变量必须拥有{@link Model}
     * @param t     当前类，this
     * @param <T>   泛型
     */
    public <T> void createModelAll(@NonNull T t) {
        MvpAnn.instanceValClass( t, (o1, f) -> getCreateModel(o1, f.getType() ) );
    }

    public <T> Object getCreateModel(@NonNull T t, Class<?> modelCls) {
        //实例化当前Field的类型
        Object model = getModel( modelCls );
        //如果存在Model则获取返回存在的model，如果不存在则创建新的Model，并且加入新的Model到管理器中
        if( model == null ) {
            //实例一个Model并且加入新的Model到管理器中，并且记录当前Holder
            addModel( t, (MvpModel)( model = MvpUtil.newClass( modelCls ) ) );
        }else {
            //持有者是否被记录到存在的Model中
            addHolder( t, modelCls );
        }
        return model;
    }

    public <T> void removeModelAll(@NonNull T t) {
        for( Field f : t.getClass().getDeclaredFields() ) {
            if( !MvpAnn.isAnnModel( f ) ) continue;
            removeModel( t, f.getType() );
        }
    }

    public int size() { return mHM.size(); }
    public void clear() { mHM.clear(); }
    public Map<String, HMInfo> getManageMap() { return mHM; }

    /**
     * Holder是否存在于Model中
     * @param holder        持有的Holder，一般为this
     * @param modelCls      Model的Class
     * @param <T>           泛型
     * @return              如果Model或者Holder不存在返回false
     */
    public <T> boolean isExistHolder(@NonNull T holder, @Nullable Class<?> modelCls) {
        if( modelCls == null ) return false;
        String holderName = holder.getClass().getName();
        String modelName = modelCls.getName();
        HMInfo info;
        if( ( info = mHM.get( modelName ) ) == null ) return false;
        return info.getHolders().contains( holderName );
    }

    /**
     * 存在的Model中添加持有者
     * @param holder        持有者。一般为this
     * @param modelCls     持有Model的Class
     * @param <T>           一般为this
     * @return              结果。0：成功；-1：异常；-2：传入参数为空
     */
    private <T> int addHolder(@NonNull T holder, @Nullable Class<?> modelCls) {
        if( modelCls == null ) return -2;
        String modelName = modelCls.getName();
        HMInfo info;
        info = mHM.get( modelName );
        if( info == null ) return -1;
        info.getHolders().add( holder.getClass().getName() );
        mHM.put( modelName, info );
        return 0;
    }

    /**
     * 移除指定model中的指定holder
     * @param holder        被移除的Holder
     * @param modelCls     需要移除Holder的Model
     * @param <T>           持有Model的Holder
     * @return              Holder持有Model的剩余数量。
     *                      -1：不存在Model，-2：传入了空参
     */
    private <T> int removeHolder(@NonNull T holder, @Nullable Class<?> modelCls) {
        if( modelCls == null ) return -2;
        String modelName = modelCls.getName();
        String holderName = holder.getClass().getName();
        HMInfo info;
        //不存在Model
        if( !mHM.containsKey( modelName ) || ( info = mHM.get( modelName )) == null ) return -1;
        info.getHolders().remove( holderName );
        mHM.put(modelName, info);
        return info.getHolders().size();
    }

    /**
     * 增加一个Model，如果只对存在的Model进行追加，可以使用{@link #addHolder(Object, Class)}
     * @param holder    持有Model的Holder，一般为this
     * @param model     持有的Model
     * @param <T>       Holder
     * @param <M>       Model
     */
    private <T, M extends MvpModel> void addModel(@NonNull T holder, @Nullable M model) {
        if( model == null ) return;
        mHM.put(
                model.getClass().getName(),
                new HMInfo( model, holder.getClass().getName() )
        );
    }

    /**
     * 移除Model。如果当前Model存在多个持有者，则只会移除当前的持有者，直到没有持有者时才会移除Model
     * @param <T>           一般为this
     * @param holder        移除Model的持有者，一般为this
     * @param modelCls      移除的Model
     */
    private <T> void removeModel(@NonNull T holder, @Nullable Class<?> modelCls) {
        if( modelCls == null ) return;
        //Holder持有Model的剩余数量。-1：不存在Model，-2：传入了空参
        if( removeHolder(holder, modelCls) == 0 ) {
            HMInfo info = mHM.remove( modelCls.getName() );
            //释放Model层可能存在的Model
            if( info != null && info.getModel() != null ) {
                info.release();
            }
        }
    }

    /**
     * 通过Model的Class获取存在的Model
     * @param modelCls       获取Model的Class
     * @return               返回存在的Model实例，不存在返回null
     */
    @Nullable
    private MvpModel getModel(@Nullable Class<?> modelCls) {
        if( modelCls == null ) return null;
        String modelName = modelCls.getName();
        if( !mHM.containsKey( modelName ) ) return null;
        HMInfo info = mHM.get( modelName );
        return info != null ? info.getModel() : null;
    }

    public static final class HMInfo {
        private MvpModel model;
        private List<String> holders = new ArrayList<>();

        HMInfo(MvpModel model, String... holders) {
            this.model = model;
            this.holders.addAll( Arrays.asList(holders) );
        }

        @NonNull
        @Override
        public String toString() {
            return "HMInfo{" +
                    "model=" + model +
                    ", holders=" + holders +
                    '}';
        }

        MvpModel getModel() { return model; }

        void release() {
            if( model == null ) return;
            model.release();
            model = null;
        }

        List<String> getHolders() { return holders; }

        HMInfo setHolders(List<String> holders) {
            this.holders = holders;
            return this;
        }
    }
}